package jgl.vajra.management.widget.retrofithelper.encrypt;


import android.text.TextUtils;
import android.util.Base64;

import com.orhanobut.logger.Logger;
import jgl.vajra.management.widget.utils.UIUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.crypto.Cipher;

/**
 * Created by Administrator on 2018/10/9.
 */


public class RSAEncryptManager {
    public static final int DEFAULT_KEY_SIZE = 2048;//秘钥默认长度
    public static final byte[] DEFAULT_SPLIT = "#PART#".getBytes();    // 当要加密的内容超过bufferSize，则采用partSplit进行分块加密
    public static final int DEFAULT_BUFFERSIZE = (DEFAULT_KEY_SIZE / 8) - 11;// 当前秘钥支持加密的最大字节数

    /**
     * 得到公钥	 * 	 * @param algorithm	 * @param bysKey	 * @return	 * @throws NoSuchAlgorithmException	 * @throws Exception
     */
    private static PublicKey getPublicKey(String algorithm, String bysKey) throws NoSuchAlgorithmException, Exception {
        byte[] decodeKey = android.util.Base64.decode(bysKey, Base64.NO_WRAP);
        X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodeKey);
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        return keyFactory.generatePublic(x509);
    }


    /*  */

    /**
     * 使用私钥解密	 * 	 * @param content	 * @return
     */
    public static String decryptByPrivateKey(String content) {
        try {
            String pubKey = getPrivateKeyFromAssets();
            RSAPrivateKey privateKey = getRSAPrivateKey(pubKey);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            byte[] plaintext = content.getBytes();
            byte[] output = cipher.doFinal(plaintext);
            return Base64.encodeToString(output, Base64.NO_WRAP);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static byte[] decryptByPrivateKey(byte[] data) throws Exception {
        String pubKey = getPrivateKeyFromAssets();
        // 得到私钥
        RSAPrivateKey keyPrivate = getRSAPrivateKey(pubKey);
        // 数据加密
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, keyPrivate);
        return cipher.doFinal(data);
    }


    public static String decryptByPrivateString(String content) {
        try {
            return new String(decrypt(Base64.decode(content, Base64.NO_WRAP), false));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 公钥分段解密
     *
     * @param encrypted 待解密数据
     */
    public static byte[] decryptByPublic(byte[] encrypted) throws Exception {
        return decrypt(encrypted, true);
    }

    /**
     * 公钥分段解密
     *
     * @param content 待解密数据
     */
    public static String decryptByPublicString(String content)  {
        try {
            return new String(decrypt(Base64.decode(content, Base64.NO_WRAP), true));
        } catch (Exception e) {
            e.printStackTrace();
            return "解密失败";
        }
    }

    // 解密
    public static byte[] decrypt(byte[] encrypted, boolean isPublic) throws Exception {
        int splitLen = DEFAULT_SPLIT.length;
        if (splitLen <= 0) {
            if (isPublic) {
                return decryptByPublicKey(encrypted);
            } else {
                return decryptByPrivateKey(encrypted);
            }
        }
        int dataLen = encrypted.length;
        List<Byte> allBytes = new ArrayList<>(1024);
        int latestStartIndex = 0;
        for (int i = 0; i < dataLen; i++) {
            byte bt = encrypted[i];
            boolean isMatchSplit = false;
            if (i == dataLen - 1) {
                // 到data的最后了
                byte[] part = new byte[dataLen - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart;
                if (isPublic) {
                    decryptPart = decryptByPublicKey(encrypted);
                } else {
                    decryptPart = decryptByPrivateKey(encrypted);
                }
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            } else if (bt == DEFAULT_SPLIT[0]) {
                // 这个是以split[0]开头
                if (splitLen > 1) {
                    if (i + splitLen < dataLen) {
                        // 没有超出data的范围
                        for (int j = 1; j < splitLen; j++) {
                            if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
                                break;
                            }
                            if (j == splitLen - 1) {
                                // 验证到split的最后一位，都没有break，则表明已经确认是split段
                                isMatchSplit = true;
                            }
                        }
                    }
                } else {
                    // split只有一位，则已经匹配了
                    isMatchSplit = true;
                }
            }
            if (isMatchSplit) {
                byte[] part = new byte[i - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart;
                if (isPublic) {
                    decryptPart = decryptByPublicKey(encrypted);
                } else {
                    decryptPart = decryptByPrivateKey(encrypted);
                }
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b;
            }
        }
        return bytes;
    }

    private static byte[] decryptByPublicKey(byte[] encrypted) throws Exception {
        String pubKey = getPublicKeyFromAssets();
        // 得到私钥
        PublicKey rsa = getPublicKey("RSA", pubKey);
        // 数据加密
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, rsa);
        return cipher.doFinal(encrypted);
    }

    /**
     * 从文件中输入流中加载公钥
     *
     * @throws Exception *             加载公钥时产生的异常
     */
    public static String getPublicKeyFromAssets() throws Exception {
        return "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxxeLN99tL26yZ7NSmZ73mo76kHFi/OXQy4E94zzVnD8o30ROjl9yT1N4QHterUFnxpbB6PTZU0fA68F+lHASVzkuQEEE+BchyjOKcQaBKT/7X7TNWjpYBUYHZ15Q8Sn4kCAvcGqhLkA33IkFdWNEoGwFHuey3BY/P3vNx2PrVT0mjYHh8hMtZzXZgbPcOam1Np5DEwtQZMBoQP6Vr+o0RQVNPFBchvwLJHChHXjq3QY+LPp0iUyNtn/AsZPekI0vMQlOQv1vZCjxKxU2ylfLHB5V0Y0JT+hEEhZ4/a5KNo0K1Ujb4gz026+qfAjOVajqTULe9zoXbQdXd2Mvq1SSVwIDAQAB";
    }

    /**
     * 从文件中输入流中加载公钥
     *
     * @throws Exception *             加载公钥时产生的异常
     */
    public static String getPublicKeyFromAssets(String fileName) throws Exception {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(UIUtils.getContext().getAssets().open(fileName)));
            String readLine = null;
            StringBuilder sb = new StringBuilder();
            while ((readLine = br.readLine()) != null) {
                sb.append(readLine);
            }
            br.close();
            return sb.toString();
        } catch (IOException e) {
            throw new Exception("公钥数据流读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥输入流为空");
        }
    }


    /**
     * 从文件中加载私钥	 * 	 * @param keyFileName	 *
     * 私钥文件名	 * @return 是否成功	 * @throws Exception
     */
    public static String getPrivateKeyFromAssets(String fileName) throws Exception {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(UIUtils.getContext().getAssets().open(fileName)));
            String readLine = null;
            StringBuilder sb = new StringBuilder();
            while ((readLine = br.readLine()) != null) {
                sb.append(readLine);
            }
            br.close();
            return sb.toString();
        } catch (IOException e) {
            throw new Exception("私钥数据读取错误");
        } catch (NullPointerException e) {
            throw new Exception("私钥输入流为空");
        }
    }

    /**
     * 从文件中加载私钥	 * 	 * @param keyFileName	 *
     * 私钥文件名	 * @return 是否成功	 * @throws Exception
     */
    public static String getPrivateKeyFromAssets() throws Exception {
        return "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxxeLN99tL26yZ7NSmZ73mo76kHFi/OXQy4E94zzVnD8o30ROjl9yT1N4QHterUFnxpbB6PTZU0fA68F+lHASVzkuQEEE+BchyjOKcQaBKT/7X7TNWjpYBUYHZ15Q8Sn4kCAvcGqhLkA33IkFdWNEoGwFHuey3BY/P3vNx2PrVT0mjYHh8hMtZzXZgbPcOam1Np5DEwtQZMBoQP6Vr+o0RQVNPFBchvwLJHChHXjq3QY+LPp0iUyNtn/AsZPekI0vMQlOQv1vZCjxKxU2ylfLHB5V0Y0JT+hEEhZ4/a5KNo0K1Ujb4gz026+qfAjOVajqTULe9zoXbQdXd2Mvq1SSVwIDAQAB";
    }

    public static RSAPrivateKey getRSAPrivateKey(String privateKeyStr) throws Exception {
        return getRSAPrivateKey(Base64.decode(privateKeyStr, Base64.NO_WRAP));
    }

    public static RSAPrivateKey getRSAPrivateKey(byte[] privateKey) throws Exception {
        try {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }

    /**
     * 使用公钥加密	 * 	 * @param content	 * @return
     */
    public static String encryptByPublicStringKey(String data) {
        return android.util.Base64.encodeToString(encryptByPublicByteKey(data.getBytes()), Base64.NO_WRAP);

    }

    /**
     * 使用公钥加密	 * 	 * @param content	 * @return
     */
    public static byte[] encryptByPublicByteKey(byte[] data) {

        try {
            String pubKey = getPublicKeyFromAssets();
            PublicKey publicKey = getPublicKey("RSA", pubKey);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher.doFinal(data);

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    public static String encryptByPublicString(String data) throws Exception {
        return android.util.Base64.encodeToString(encryptByte(data.getBytes(), true), android.util.Base64.NO_WRAP);
    }

    /**
     * 用公钥对字符串进行分段加密
     */
    public static String encryptByPrivateString(String data) throws Exception {
        return android.util.Base64.encodeToString(encryptByte(data.getBytes(), false), Base64.NO_WRAP);
    }

    /**
     * 用公钥对字符串进行分段加密
     */
    public static byte[] encryptByte(byte[] data, boolean isPublic) throws Exception {
        int dataLen = data.length;
        if (dataLen <= DEFAULT_BUFFERSIZE) {
            if (isPublic) {
                return encryptByPublicByteKey(data);
            } else {
                return encryptByPrivateByteKey(data);
            }
        }
        List<Byte> allBytes = new ArrayList<>(2048);
        int bufIndex = 0;
        int subDataLoop = 0;
        byte[] buf = new byte[DEFAULT_BUFFERSIZE];
        for (int i = 0; i < dataLen; i++) {
            buf[bufIndex] = data[i];
            if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) {
                subDataLoop++;
                if (subDataLoop != 1) {
                    for (byte b : DEFAULT_SPLIT) {
                        allBytes.add(b);
                    }
                }
                byte[] encryptBytes;
                if (isPublic) {
                    encryptBytes = encryptByPublicByteKey(data);
                } else {
                    encryptBytes = encryptByPrivateByteKey(data);
                }
                for (byte b : encryptBytes) {
                    allBytes.add(b);
                }
                bufIndex = 0;
                if (i == dataLen - 1) {
                    buf = null;
                } else {
                    buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)];
                }
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b;
            }
        }
        return bytes;
    }


    public static String getSign(Map<String, String> map, String rsaKey, boolean rsa2) throws UnsupportedEncodingException {
        List<String> keys = new ArrayList<>(map.keySet());
        // key排序
        Collections.sort(keys);

        StringBuilder authInfo = new StringBuilder();
       /* for (int i = 0; i < keys.size() - 1; i++) {
            String key = keys.get(i);
            String value = map.get(key);
            authInfo.append(buildKeyValue(key, value, false));
            authInfo.append("&");
        }

        String tailKey = keys.get(keys.size() - 1);
        String tailValue = map.get(tailKey);
        authInfo.append(buildKeyValue(tailKey, tailValue, false));*/
        int sortParamSize = keys.size();
        //step1.3   将第2步中排序后的参数(key=value)用&拼接起来，并进行URL编码。
        for (int i = 0; i < sortParamSize; i++) {
            String value = map.get(keys.get(i));
            if (!TextUtils.isEmpty(value)) {
                authInfo.append(keys.get(i) + "=" + value);
                if (i != sortParamSize - 1) {
                    authInfo.append("&");
                }

            }
        }
        Logger.d("数据字典升序并encode=" + authInfo.toString());

     /*   try {
            encodedSign = URLEncoder.encode(oriSign, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }*/
        return sign(authInfo.toString(), rsaKey, rsa2);
    }

    /**
     * 拼接键值对
     *
     * @param key
     * @param value
     * @param isEncode
     * @return
     */
    private static String buildKeyValue(String key, String value, boolean isEncode) {
        StringBuilder sb = new StringBuilder();
        sb.append(key);
        sb.append("=");
        if (isEncode) {
            try {
                sb.append(URLEncoder.encode(value, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                sb.append(value);
            }
        } else {
            sb.append(value);
        }
        return sb.toString();
    }

    private static final String ALGORITHM = "RSA";

    private static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";

    private static final String DEFAULT_CHARSET = "UTF-8";

    private static String getAlgorithms(boolean rsa2) {
        return rsa2 ? SIGN_SHA256RSA_ALGORITHMS : SIGN_ALGORITHMS;
    }

    /**
     * 使用私钥加密	 * 	 * @param content	 * @return
     */
    public static byte[] encryptByPrivateByteKey(byte[] data) {

        try {
            String privateKet = getPrivateKeyFromAssets();
            RSAPrivateKey rsaPrivateKey = getRSAPrivateKey(privateKet);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, rsaPrivateKey);
            return cipher.doFinal(data);

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }



    public static String sign(String content, String privateKey, boolean rsa2) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
                    Base64.decode(privateKey, Base64.NO_WRAP));
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, "BC");
            PrivateKey priKey = keyFactory.generatePrivate(priPKCS8);

            java.security.Signature signature = java.security.Signature
                    .getInstance(getAlgorithms(rsa2));

            signature.initSign(priKey);
            signature.update(content.getBytes(DEFAULT_CHARSET));

            byte[] signed = signature.sign();

            return Base64.encodeToString(signed, Base64.NO_WRAP);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }


}

